﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace ErickOrlando.Utilidades.Data
{
    /// <summary>
    /// Contiene funciones de extensión que
    /// permiten modificar colecciones genéricas
    /// </summary>
    /// <remarks>Erick Orlando © 2011</remarks>
    public static class Colecciones
    {

        #region     Mover Abajo 

        /// <summary>
        /// Mueve un elemento hacia abajo en la colección en base al índice
        /// </summary>
        /// <typeparam name="T">Coleccion</typeparam>
        /// <param name="source">Instancia de la colección</param>
        /// <param name="index">Índice que se moverá</param>
        /// <returns>Retorna la colección modificada</returns>
        /// <remarks>Erick Orlando © 2011</remarks>
        public static IEnumerable<T> MoveDown<T>(this IEnumerable<T> source, int index)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            var array = source.ToArray();
            if (index == array.Length - 1)
            {
                return source;
            }
            return Swap<T>(array, index, index + 1);
        }

        /// <summary>
        /// Mueve un elemento hacia abajo en la colección en base a la instancia
        /// </summary>
        /// <typeparam name="T">Colección</typeparam>
        /// <param name="source">Instancia de la colección</param>
        /// <param name="item">Elemento que se moverá</param>
        /// <returns>Retorna la colección modificada</returns>
        /// <remarks>Erick Orlando © 2011</remarks>
        public static IEnumerable<T> MoveDown<T>(this IEnumerable<T> source, T item)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            T[] array__1 = source.ToArray();
            int index = Array.FindIndex(array__1, i => i.Equals(item));
            if (index == -1)
            {
                throw new InvalidOperationException();
            }
            if (index == array__1.Length - 1)
            {
                return source;
            }
            return Swap<T>(array__1, index, index + 1);
        }

        #endregion

        #region     Mover Arriba 
        /// <summary>
        /// Mueve un elemento hacia arriba en la colección en base al índice
        /// </summary>
        /// <typeparam name="T">Colección</typeparam>
        /// <param name="source">Instancia de la colección</param>
        /// <param name="index">Indice que se moverá</param>
        /// <returns>Retorna la colección modificada</returns>
        /// <remarks>Erick Orlando © 2011</remarks>
        public static IEnumerable<T> MoveUp<T>(this IEnumerable<T> source, int index)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            T[] array = source.ToArray();
            if (index == 0)
            {
                return source;
            }
            return Swap<T>(array, index - 1, index);
        }

        /// <summary>
        /// Mueve un elemento hacia arriba en la colección en base a la instancia
        /// </summary>
        /// <typeparam name="T">Colección</typeparam>
        /// <param name="source">Instancia de la colección</param>
        /// <param name="item">Elemento que se moverá</param>
        /// <returns>Retorna la colección modificada</returns>
        /// <remarks>Erick Orlando © 2011</remarks>
        public static IEnumerable<T> MoveUp<T>(this IEnumerable<T> source, T item)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            T[] array__1 = source.ToArray();
            int index = Array.FindIndex(array__1, i => i.Equals(item));
            if (index == -1)
            {
                throw new InvalidOperationException();
            }
            if (index == 0)
            {
                return source;
            }
            return Swap<T>(array__1, index - 1, index);
        }

        #endregion

        #region     Función Swap 

        /// <summary>
        /// Intercambia la posición de los elementos en una colección
        /// </summary>
        /// <typeparam name="T">Colección</typeparam>
        /// <param name="source">Instancia de la colección</param>
        /// <param name="firstIndex">Primer índice</param>
        /// <param name="secondIndex">Segundo índice</param>
        /// <returns>Retorna la colección modificada</returns>
        /// <remarks>Erick Orlando © 2011</remarks>
        public static IEnumerable<T> Swap<T>(this IEnumerable<T> source, int firstIndex, int secondIndex)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            T[] array = source.ToArray();
            return Swap<T>(array, firstIndex, secondIndex);
        }

        /// <summary>
        /// Intercambia la posición de los elementos en una colección
        /// </summary>
        /// <typeparam name="T">Colección</typeparam>
        /// <param name="array">Array de la colección</param>
        /// <param name="firstIndex">Primer índice</param>
        /// <param name="secondIndex">Segundo índice</param>
        /// <returns>Retorna la colección modificada</returns>
        /// <remarks>Erick Orlando © 2011</remarks>
        private static IEnumerable<T> Swap<T>(T[] array, int firstIndex, int secondIndex)
        {
            if (firstIndex < 0 || firstIndex >= array.Length)
            {
                throw new ArgumentOutOfRangeException("firstIndex");
            }
            if (secondIndex < 0 || secondIndex >= array.Length)
            {
                throw new ArgumentOutOfRangeException("secondIndex");
            }
            T tmp = array[firstIndex];
            array[firstIndex] = array[secondIndex];
            array[secondIndex] = tmp;
            return array;
        }

        /// <summary>
        /// Intercambia la posición de los elementos en una colección
        /// </summary>
        /// <typeparam name="T">Colección</typeparam>
        /// <param name="source">Instancia de la colección</param>
        /// <param name="firstItem">Primer elemento</param>
        /// <param name="secondItem">Segundo elemento</param>
        /// <returns>Retorna la colección modificada</returns>
        /// <remarks>Erick Orlando © 2011</remarks>
        public static IEnumerable<T> Swap<T>(this IEnumerable<T> source, T firstItem, T secondItem)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            T[] array__1 = source.ToArray();
            int firstIndex = Array.FindIndex(array__1, i => i.Equals(firstItem));
            int secondIndex = Array.FindIndex(array__1, i => i.Equals(secondItem));
            return Swap(array__1, firstIndex, secondIndex);
        }

        #endregion

    }
}
